
module display(
    input clk, // 时钟信号
	input rst_n, // 复位信号，低电平有效
    input [5:0] apple_x, // 苹果的X坐标
    input [5:0] apple_y, // 苹果的Y坐标
    input [32*7-1:0] snake_x_bus, // 蛇各段的X坐标总线
    input [32*7-1:0] snake_y_bus, // 蛇各段的Y坐标总线
    input [31:0] snake_piece_is_display, // 标记蛇各段是否显示
    input [1:0] game_status, // 游戏状态
	input [1:0] current_direction, // 当前方向
	input [1:0] game_level, // 游戏难度级别

	input [7:0] score, // 得分
	input [11:0] run_time, // 运行时间
    
    output vga_hsync, // VGA水平同步信号
	output vga_vsync, // VGA垂直同步信号
	output vga_de, // VGA数据使能信号
    output [23:0] vga_rgb // VGA输出颜色
);

// ************ 参数定义 ***********

// 游戏状态
parameter LAUNCHING=2'b00; // 启动状态
parameter PLAYING=2'b01; // 游戏进行中
parameter DIE_FLASHING=2'b10; // 死亡闪烁状态
parameter INITIALIZING=2'b11; // 初始化状态

// 屏幕边界参数
parameter BLOCK_X_MAX = 7'd79; // X轴最大值
parameter BLOCK_Y_MAX = 7'd59; // Y轴最大值

// 控制按钮参数
parameter UP=2'b00; // 上
parameter RIGHT=2'b01; // 右
parameter DOWN=2'b10; // 下
parameter LEFT=2'b11; // 左

// 蛇的最大长度
parameter SNAKE_MAX_LEN = 32;

// 图像参数
parameter PIC_H_MAX = 11'd128; // 图像高度
parameter PIC_V_MAX = 11'd128; // 图像宽度
parameter PIC_SIZE = 14'd16383; // 图像大小

// VGA参数
parameter VGA_H = 11'd1280; // VGA水平分辨率
parameter VGA_V = 11'd960; // VGA垂直分辨率

// 视频RGB寄存器和线网
reg [23:0] video_rgb; // 视频颜色寄存器
wire video_hs; // 视频水平同步线网
wire video_vs; // 视频垂直同步线网
wire video_de; // 视频数据使能线网

// 像素位置线网
wire  [10:0]  pixel_xpos_w; // 像素X位置
wire  [10:0]  pixel_ypos_w; // 像素Y位置
wire  [23:0]  pixel_data_w; // 像素数据
wire pixel_data_req;	// 像素数据请求信号

// 块索引线网
wire [6:0] current_x; // 当前块X索引
wire [6:0] current_y; // 当前块Y索引

// 蛇索引线网
wire [6:0] snake_x [SNAKE_MAX_LEN-1:0]; // 蛇各段X索引
wire [6:0] snake_y [SNAKE_MAX_LEN-1:0]; // 蛇各段Y索引

// 块属性线网
wire is_wall; // 是否为墙
wire is_apple; // 是否为苹果
wire is_snake_head; // 是否为蛇头
wire is_snake_body; // 是否为蛇身

// 方向线网
wire is_up; // 是否向上
wire is_down; // 是否向下
wire is_left; // 是否向左
wire is_right; // 是否向右

// 分数和时间线网
wire is_score_l; // 是否为分数低位
wire is_score_h; // 是否为分数高位

wire is_time_l; // 是否为时间低位
wire is_time_m; // 是否为时间中位
wire is_time_h; // 是否为时间高位

wire is_time; // 是否为时间
wire is_points; // 是否为得分

// 蛇图标寄存器和线网
reg [13:0] icon_addr; // 图标地址寄存器
wire [11:0] icon_data; // 图标数据线网
reg icon_valid; // 图标有效标志寄存器

// 蛇Logo寄存器和线网
wire [6:0] logo_addr; // Logo地址线网
reg [15:0] logo_data; // Logo数据寄存器
reg logo_valid; // Logo有效标志寄存器

// 块寄存器和线网
wire [7:0] block_addr; // 块地址线网
wire [11:0] block_data; // 块数据线网
reg block_valid; // 块有效标志寄存器

// 数字寄存器和线网
reg number_valid_r; // 数字有效标志寄存器
wire [3:0] number_addr; // 数字地址线网
reg [15:0] number_data; // 数字数据寄存器

//*************************** 生成VGA水平和垂直同步信号

assign vga_hsync = video_hs; // VGA水平同步信号赋值
assign vga_vsync = video_vs; // VGA垂直同步信号赋值
assign vga_de = video_de; // VGA数据使能信号赋值
assign vga_rgb = video_rgb; // VGA颜色信号赋值

// VGA驱动实例化
vga_driver u_vga_driver
(
    .pixel_clk      (clk), // 像素时钟
    .sys_rst_n      (rst_n), // 系统复位信号

    .video_hs       (video_hs), // 视频水平同步信号
    .video_vs       (video_vs), // 视频垂直同步信号
    .video_de       (video_de), // 视频数据使能信号
    .video_rgb      ( ), // 视频颜色信号

	.pixel_data_req	(pixel_data_req), // 像素数据请求信号
    .pixel_xpos     (pixel_xpos_w), // 像素X位置
    .pixel_ypos     (pixel_ypos_w), // 像素Y位置
    .pixel_data     ('b0) // 像素数据
);

// 当前块索引赋值
assign current_x = pixel_xpos_w[10:4]; // 当前块X索引
assign current_y = pixel_ypos_w[10:4]; // 当前块Y索引
	 
// 蛇各段索引生成
genvar i;
generate
	for(i=0;i<SNAKE_MAX_LEN;i=i+1)begin:loop
		assign snake_x[i] = snake_x_bus[7*i+:7]; // 蛇各段X索引赋值
		assign snake_y[i] = snake_y_bus[7*i+:7]; // 蛇各段Y索引赋值
	end
endgenerate

//*************************** 生成VGA帧 **************

// 墙判定
assign is_wall = game_level == 'd0 ? 
                 ((current_x==0 || current_x==BLOCK_X_MAX || current_y==0 || current_y == BLOCK_Y_MAX) && pixel_data_req) 
                 : 
                 ((current_x==0 || current_x==BLOCK_X_MAX || current_y==0 || current_y == BLOCK_Y_MAX 
                   || (current_y==BLOCK_Y_MAX/3 && current_x>=BLOCK_X_MAX/4 && current_x<=BLOCK_X_MAX/4*3)
                   || (current_x==BLOCK_X_MAX/4 && current_y>=BLOCK_Y_MAX/3 && current_y<=BLOCK_Y_MAX/3*2)
                   || (current_x==BLOCK_X_MAX/4*3 && current_y>=BLOCK_Y_MAX/3 && current_y<=BLOCK_Y_MAX/3*2)
                   ) && pixel_data_req)
                 ;

// 苹果判定
assign is_apple = current_x == apple_x && current_y == apple_y && pixel_data_req;

// 蛇头判定
assign is_snake_head = current_x == snake_x[0] && current_y == snake_y[0] && pixel_data_req;

// 蛇身判定
assign is_snake_body = (
			(current_x == snake_x[1] && current_y == snake_y[1] && snake_piece_is_display[1]) ||
			(current_x == snake_x[2] && current_y == snake_y[2] && snake_piece_is_display[2]) ||
			(current_x == snake_x[3] && current_y == snake_y[3] && snake_piece_is_display[3]) ||
			(current_x == snake_x[4] && current_y == snake_y[4] && snake_piece_is_display[4]) ||
			(current_x == snake_x[5] && current_y == snake_y[5] && snake_piece_is_display[5]) ||
			(current_x == snake_x[6] && current_y == snake_y[6] && snake_piece_is_display[6]) ||
			(current_x == snake_x[7] && current_y == snake_y[7] && snake_piece_is_display[7]) ||
			(current_x == snake_x[8] && current_y == snake_y[8] && snake_piece_is_display[8]) ||
			(current_x == snake_x[9] && current_y == snake_y[9] && snake_piece_is_display[9]) ||
			(current_x == snake_x[10] && current_y == snake_y[10] && snake_piece_is_display[10]) ||
			(current_x == snake_x[11] && current_y == snake_y[11] && snake_piece_is_display[11]) ||
			(current_x == snake_x[12] && current_y == snake_y[12] && snake_piece_is_display[12]) ||
			(current_x == snake_x[13] && current_y == snake_y[13] && snake_piece_is_display[13]) ||
			(current_x == snake_x[14] && current_y == snake_y[14] && snake_piece_is_display[14]) ||
			(current_x == snake_x[15] && current_y == snake_y[15] && snake_piece_is_display[15]) ||
			(current_x == snake_x[16] && current_y == snake_y[16] && snake_piece_is_display[16]) ||
			(current_x == snake_x[17] && current_y == snake_y[17] && snake_piece_is_display[17]) ||
			(current_x == snake_x[18] && current_y == snake_y[18] && snake_piece_is_display[18]) ||
			(current_x == snake_x[19] && current_y == snake_y[19] && snake_piece_is_display[19]) ||
			(current_x == snake_x[20] && current_y == snake_y[20] && snake_piece_is_display[20]) ||
			(current_x == snake_x[21] && current_y == snake_y[21] && snake_piece_is_display[21]) ||
			(current_x == snake_x[22] && current_y == snake_y[22] && snake_piece_is_display[22]) ||
			(current_x == snake_x[23] && current_y == snake_y[23] && snake_piece_is_display[23]) ||
			(current_x == snake_x[24] && current_y == snake_y[24] && snake_piece_is_display[24]) ||
			(current_x == snake_x[25] && current_y == snake_y[25] && snake_piece_is_display[25]) ||
			(current_x == snake_x[26] && current_y == snake_y[26] && snake_piece_is_display[26]) ||
			(current_x == snake_x[27] && current_y == snake_y[27] && snake_piece_is_display[27]) ||
			(current_x == snake_x[28] && current_y == snake_y[28] && snake_piece_is_display[28]) ||
			(current_x == snake_x[29] && current_y == snake_y[29] && snake_piece_is_display[29]) ||
			(current_x == snake_x[30] && current_y == snake_y[30] && snake_piece_is_display[30]) ||
			(current_x == snake_x[31] && current_y == snake_y[31] && snake_piece_is_display[31])
			) && pixel_data_req;

// 方向判定
assign is_up = is_snake_head && current_direction == UP;
assign is_down = is_snake_head && current_direction == DOWN;
assign is_left = is_snake_head && current_direction == LEFT;
assign is_right = is_snake_head && current_direction == RIGHT;

// 根据游戏状态设置视频颜色
always @(posedge clk) begin
	case(game_status)
		LAUNCHING:begin
			if(pixel_data_req)begin
				if(icon_valid)
					video_rgb<={icon_data[11:8],4'b0,icon_data[7:4],4'b0,icon_data[3:0],4'b0}; // 显示图标
				else if(logo_valid)
					video_rgb<={logo_data[15:11],3'b0,logo_data[10:5],2'b0,logo_data[4:0],3'b0}; // 显示Logo
				else if(is_wall)begin
					video_rgb<={block_data[11:8],4'b0,block_data[7:4],4'b0,block_data[3:0],4'b0}; // 显示墙
				end
				else
					video_rgb<={8'hff,8'hff,8'hff}; // 白色背景
			end
			else video_rgb<={8'h00,8'h00,8'h00}; // 黑色背景
		end
		INITIALIZING:begin
			if(pixel_data_req)begin
				if(icon_valid)
					video_rgb<={icon_data[11:8],4'b0,icon_data[7:4],4'b0,icon_data[3:0],4'b0}; // 显示图标
				else if(logo_valid)
					video_rgb<={logo_data[15:11],3'b0,logo_data[10:5],2'b0,logo_data[4:0],3'b0}; // 显示Logo
				else if(number_valid_r)
					video_rgb<={number_data[15:11],3'b0,number_data[10:5],2'b0,number_data[4:0],3'b0}; // 显示数字
				else if(is_wall)begin
					video_rgb<={block_data[11:8],4'b0,block_data[7:4],4'b0,block_data[3:0],4'b0}; // 显示墙
				end
				else
					video_rgb<={8'hff,8'hff,8'hff}; // 白色背景
			end
			else video_rgb<={8'hff,8'h00,8'h00}; // 红色背景
		end
		PLAYING:begin
			if(number_valid_r)
				video_rgb<={number_data[15:11],3'b0,number_data[10:5],2'b0,number_data[4:0],3'b0}; // 显示数字
			else if(block_valid)begin
				video_rgb<={block_data[11:8],4'b0,block_data[7:4],4'b0,block_data[3:0],4'b0}; // 显示块
			end
			else begin
				video_rgb<={8'h0,8'h0,8'h0}; // 黑色背景
			end
		end
		DIE_FLASHING:begin
			if(number_valid_r)
				video_rgb<={number_data[15:11],3'b0,number_data[10:5],2'b0,number_data[4:0],3'b0}; // 显示数字
			else if(block_valid)begin
				video_rgb<={block_data[11:8],4'b0,block_data[7:4],4'b0,block_data[3:0],4'b0}; // 显示块
			end
			else begin
				video_rgb<={8'h0,8'h0,8'h0}; // 黑色背景
			end
		end		
		default:begin
			video_rgb<={8'h0,8'h0,8'h0}; // 黑色背景
		end
	endcase
end

//******************************* 蛇图标

// 图标读取使能信号
wire icon_rd_en; 
wire [14:0] icon_addr_t;

// 图标读取使能信号生成
assign  icon_rd_en = (((pixel_xpos_w >= (((VGA_H - PIC_H_MAX)/2) - 2'd1))
                && (pixel_xpos_w < (((VGA_H - PIC_H_MAX)/2) + PIC_H_MAX - 2'd1))) 
                &&((pixel_ypos_w >= ((VGA_V - PIC_V_MAX)/2 - 64))
                && ((pixel_ypos_w < (((VGA_V - PIC_V_MAX)/2) + PIC_V_MAX - 64 )))));

// 时钟上升沿或复位信号下降沿触发
always@(posedge clk or negedge rst_n)
    if(rst_n == 1'b0)  // 复位信号有效
        icon_addr    <=  14'd0;  // 初始化图标地址为0
    else if(icon_addr == PIC_SIZE)  // 图标地址达到最大值
        icon_addr    <=  14'd0;  // 重置图标地址为0
    else if(icon_rd_en == 1'b1)  // 图标读使能信号有效
        icon_addr    <=  icon_addr + 1'b1;  // 增加图标地址

// 时钟上升沿或复位信号下降沿触发
always@(posedge clk or negedge rst_n)
    if(rst_n == 1'b0)  // 复位信号有效
        icon_valid    <=  'd0;  // 初始化图标有效信号为0
    else
        icon_valid    <=  icon_rd_en;  // 更新图标有效信号

// 根据游戏状态选择图标地址
assign icon_addr_t = game_status == LAUNCHING ? {1'b0,icon_addr} : {1'b1,icon_addr};

// 定义图标数据线
wire [8:0] icon_data_t;

// 将图标数据进行扩展
assign icon_data = {icon_data_t[8:6],1'b0,icon_data_t[5:3],1'b0,icon_data_t[2:0],1'b0};

// 图标ROM实例化
icon_rom u_icon_rom
(
    .clk(clk),  // 时钟信号
    .addr(icon_addr_t),  // 图标地址
    .data(icon_data_t)  // 图标数据
);

// ******************************* 蛇标志

// 参数定义
localparam  CHAR_B_H=   ((VGA_H - PIC_H_MAX)/2)-10,  // 显示字符的起始X坐标
            CHAR_B_V=   ((VGA_V - PIC_V_MAX)/2 - 64)+128-1 ;  // 显示字符的起始Y坐标
localparam   CHAR_W  =   11'd144,  // 字符宽度
            CHAR_H  =   11'd96;  // 字符高度

localparam  BLACK   =   16'h0000,  // 黑色
            WHITE   =   16'b1111_0111_1001_1110,  // 白色
            GOLDEN  =   16'hFEC0,  // 金色
            RED     =   16'b11111_000000_00000,  // 红色
            BLUE    =   16'b00000_000000_11111;  // 蓝色

// 定义显示字符的X和Y坐标
wire [10:0] char_x;  // 显示字符的X坐标
wire [10:0] char_y;  // 显示字符的Y坐标

// 定义标志数据线
wire [144-1:0] logo_data_t;

// 定义标志地址寄存器
reg [6:0] logo_addr_r;

// 定义显示字符的X坐标寄存器
reg [10:0] char_x_r;

// 定义标志读使能信号
wire logo_rd_en;

// 定义标志读使能信号寄存器
reg logo_rd_en_r;

// 判断是否在显示字符区域内
assign logo_rd_en = (((pixel_xpos_w >= CHAR_B_H) && (pixel_xpos_w < (CHAR_B_H + CHAR_W)))
                    && ((pixel_ypos_w >= CHAR_B_V) && (pixel_ypos_w < (CHAR_B_V + CHAR_H))));

// 计算显示字符的X和Y坐标
assign  char_x  =   logo_rd_en ? (pixel_xpos_w - CHAR_B_H) : 11'h3FF;
assign  char_y  =   logo_rd_en ? (pixel_ypos_w - CHAR_B_V) : 11'h3FF;

// 时钟上升沿或复位信号下降沿触发
always@(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0) begin  // 复位信号有效
        logo_valid    <=  'd0;  // 初始化标志有效信号为0
        logo_rd_en_r  <=  'd0;  // 初始化标志读使能信号寄存器为0
        logo_addr_r   <= 'b0;  // 初始化标志地址寄存器为0
        char_x_r      <= 'b0;  // 初始化显示字符的X坐标寄存器为0
    end
    else begin
        logo_valid    <=  logo_rd_en_r;  // 更新标志有效信号
        logo_rd_en_r  <=  logo_rd_en;  // 更新标志读使能信号寄存器
        logo_addr_r   <= logo_addr;  // 更新标志地址寄存器
        char_x_r      <= char_x;  // 更新显示字符的X坐标寄存器
    end
end

// 时钟上升沿或复位信号下降沿触发
always@(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)  // 复位信号有效
        logo_data    <= WHITE;  // 初始化标志数据为白色
    else if(logo_rd_en_r && logo_data_t[char_x_r] && ~logo_addr_r[5] && ~logo_addr_r[6]) begin
        logo_data    <=  GOLDEN;  // 设置标志数据为金色
    end
    else if(logo_rd_en_r && ~logo_data_t[char_x_r] && ~logo_addr_r[5] && ~logo_addr_r[6]) begin
        logo_data    <=  BLUE;  // 设置标志数据为蓝色
    end
    else if(logo_rd_en_r && (logo_data_t[char_x_r]) && ~logo_addr_r[5] && logo_addr_r[6]) begin
        logo_data    <=  GOLDEN;  // 设置标志数据为金色
    end
    else if(logo_rd_en_r && (~logo_data_t[char_x_r]) && ~logo_addr_r[5] && logo_addr_r[6]) begin
        logo_data    <=  RED;  // 设置标志数据为红色
    end
    else if(logo_rd_en_r && ~logo_data_t[char_x_r] && logo_addr_r[5] && ~logo_addr_r[6]) begin
        logo_data    <=  BLACK;  // 设置标志数据为黑色
    end
    else
        logo_data    <=  WHITE;  // 设置标志数据为白色
end

// 计算标志地址
assign logo_addr = char_y[6:0];

// 标志ROM实例化
logo_rom u_logo_rom
(
    .clk(clk),  // 时钟信号
    .addr(logo_addr),  // 标志地址
    .data(logo_data_t)  // 标志数据
);

// 元素

// 计算块地址
assign block_addr =  + {pixel_ypos_w[3:0],pixel_xpos_w[3:0]};

// 时钟上升沿或复位信号下降沿触发
always@(posedge clk or negedge rst_n)
    if(rst_n == 1'b0)  // 复位信号有效
        block_valid    <=  'd0;  // 初始化块有效信号为0
    else if(is_wall || is_apple || is_up || is_down || is_right || is_left || is_snake_body || is_score_h || is_score_l || is_time_h || is_time_m || is_time_l || is_time || is_points)
        block_valid    <=  1'b1;  // 设置块有效信号为1
    else
        block_valid    <=  'd0;  // 设置块有效信号为0

// 元素ROM实例化
element_rom u_element_rom
(
    .clk(clk),  // 时钟信号
    .is_wall(is_wall),  // 是否为墙
    .is_apple(is_apple),  // 是否为苹果
    .is_left(is_left),  // 是否为左方向
    .is_up(is_up),  // 是否为上方向
    .is_right(is_right),  // 是否为右方向
    .is_down(is_down),  // 是否为下方向
    .is_body(is_snake_body),  // 是否为蛇身体
    .is_number(is_score_h || is_score_l || is_time_h || is_time_m || is_time_l),  // 是否为数字
    .is_time(is_time),  // 是否为时间
    .is_points(is_points),  // 是否为分数
    .number(number_addr),  // 数字地址
    .addr(block_addr),  // 块地址
    .data(block_data)  // 块数据
);

// ********************* 分数和运行时间 ******************

// 判断是否为分数或时间位置
assign is_points = current_x == (BLOCK_X_MAX/2-3) && current_y == BLOCK_Y_MAX && pixel_data_req;
assign is_score_h = current_x == (BLOCK_X_MAX/2-2) && current_y == BLOCK_Y_MAX && pixel_data_req;
assign is_score_l = current_x == (BLOCK_X_MAX/2-1) && current_y == BLOCK_Y_MAX && pixel_data_req;
assign is_time = current_x == (BLOCK_X_MAX/2 + 2) && current_y == BLOCK_Y_MAX && pixel_data_req;
assign is_time_h = current_x == (BLOCK_X_MAX/2 + 3) && current_y == BLOCK_Y_MAX && pixel_data_req;
assign is_time_m = current_x == (BLOCK_X_MAX/2 + 4) && current_y == BLOCK_Y_MAX && pixel_data_req;
assign is_time_l = current_x == (BLOCK_X_MAX/2 + 5) && current_y == BLOCK_Y_MAX && pixel_data_req;

// 选择数字地址
assign number_addr = is_score_h ? score[7:4] : 
                     is_score_l ? score[3:0] :
                     is_time_h ? run_time[11:8] :
                     is_time_m ? run_time[7:4] :
                     run_time[3:0];

endmodule
